<!DOCTYPE html>
<body>
<script src="../../../resources/testharness.js"></script>
<script src="../../../resources/testharnessreport.js"></script>
<script>
test(drawThenGetImageData,
    'tests creating ImageData using several methods, calling putImageData, and reading back the color.');

function populate_array(data, data_color_space, data_storage_format) {
  for (var i = 0; i < data.length / 4; ++i) {
    var color_srgb_f32 = [50/255., 100/255., 150/255., 1.0];
    var color_p3_f32 = [0.24304, 0.38818, 0.57227, 1.0];
    var color_rec2020_f32 = [0.34106, 0.41553, 0.59180, 1.0];

    if (data_color_space == 'srgb')
        data_color = color_srgb_f32;
    else if (data_color_space == 'display-p3')
        data_color = color_p3_f32;
    else if (data_color_space == 'rec2020')
        data_color = color_rec2020_f32;

    var multiplier = 1;
    if (data_storage_format == 'uint8')
      multiplier = 255;
    if (data_storage_format == 'uint16')
      multiplier = 65535;
    if (data_storage_format == 'float32')
      multiplier = 1.0;

    data[4*i + 0] = multiplier * data_color[0];
    data[4*i + 1] = multiplier * data_color[1];
    data[4*i + 2] = multiplier * data_color[2];
    data[4*i + 3] = multiplier * data_color[3];
  }
}

function run_test(canvas_color_space, canvas_pixel_format, data_color_space, data_storage_format) {
  var canvas = document.createElement('canvas');
  canvas.width = 20;
  canvas.height = 16;
  var ctx = canvas.getContext('2d', {colorSpace:canvas_color_space, pixelFormat:canvas_pixel_format});

  const num_subtests = 5;
  const data_width = Math.floor(canvas.width / num_subtests);
  const data_height = canvas.height;
  subtest_name = '';

  // Create an ImageData using createImageData and populate its data array.
  {
    var data_to_put = ctx.createImageData(data_width, data_height, {colorSpace:data_color_space, storageFormat:data_storage_format});
    populate_array(data_to_put.data, data_color_space, data_storage_format);
    ctx.putImageData(data_to_put, 0 * data_width, 0);
    subtest_name = 'createImageData';
  }

  // Create an ImageData using a constructor and populate its data array.
  {
    var data_to_put = new ImageData(data_width, data_height, {colorSpace:data_color_space, storageFormat:data_storage_format});
    populate_array(data_to_put.data, data_color_space, data_storage_format);
    ctx.putImageData(data_to_put, 1 * data_width, 0);
    subtest_name = 'constructor with width,height';
  }

  // Create and populate an array, then use that array to create an ImageData.
  {
    var length = 4 * data_width * data_height;
    var data = [];
    if (data_storage_format == 'uint8')
      data = new Uint8ClampedArray(length);
    if (data_storage_format == 'uint16')
      data = new Uint16Array(length);
    if (data_storage_format == 'float32')
      data = new Float32Array(length);
    populate_array(data, data_color_space, data_storage_format);

    var data_to_put = new ImageData(data, data_width, data_height, {colorSpace:data_color_space, storageFormat:data_storage_format});
    ctx.putImageData(data_to_put, 2 * data_width, 0);
    subtest_name = 'constructor with data,width,height,settings';
  }

  // Create and populate an array, then use that array to create an ImageData
  // without specifying the height. Note that this will also not specify the
  // ImageDataSettings, so the color space will implicitly be sRGB.
  {
    var length = 4 * data_width * data_height;
    var data = [];
    if (data_storage_format == 'uint8')
      data = new Uint8ClampedArray(length);
    if (data_storage_format == 'uint16')
      data = new Uint16Array(length);
    if (data_storage_format == 'float32')
      data = new Float32Array(length);
    populate_array(data, 'srgb', data_storage_format);

    var data_to_put = new ImageData(data, data_width);
    ctx.putImageData(data_to_put, 3 * data_width, 0);
    subtest_name = 'constructor with data,width';
  }

  // Create and populate an array, then use that array to create an ImageData
  // specifying the height but not ImageDataSettings. Note that this will also
  // not specify the ImageDataSettings, so the color space will implicitly be
  // sRGB.
  {
    var length = 4 * data_width * data_height;
    var data = [];
    if (data_storage_format == 'uint8')
      data = new Uint8ClampedArray(length);
    if (data_storage_format == 'uint16')
      data = new Uint16Array(length);
    if (data_storage_format == 'float32')
      data = new Float32Array(length);
    populate_array(data, 'srgb', data_storage_format);

    var data_to_put = new ImageData(data, data_width, data_height);
    ctx.putImageData(data_to_put, 4 * data_width, 0);
    subtest_name = 'constructor with data,width';
  }

  // Read back as 8-bit sRGB.
  for (var j = 0; j < num_subtests; ++j) {
    var data_from_get = ctx.getImageData(data_width * (j + 0.5), data_height / 2, 1, 1, {colorSpace:'srgb'});
    var passed = true;
    var pixel_expected = [50, 100, 150, 255];
    var pixel_actual = data_from_get.data;
    for (var i = 0; i < 4; ++i) {
      var epsilon = 2;
      if (Math.abs(pixel_actual[i] - pixel_expected[i]) > epsilon)
        passed = false;
    }
    if (!passed) {
      console.log('Failure! ' + subtest_name);
      console.log('  Canvas:    ' + canvas_color_space + ',' + canvas_pixel_format);
      console.log('  ImageData: ' + data_color_space + ',' + data_storage_format);
      console.log('  Expected:  ' + pixel_expected);
      console.log('  Actual:    ' + [pixel_actual[0], pixel_actual[1], pixel_actual[2], pixel_actual[3]]);
      console.log('  Epsilon:   ' + epsilon);
    }
  }

  return passed;
}

function drawThenGetImageData() {
  color_spaces = ['srgb', 'display-p3', 'rec2020'];
  canvas_pixel_formats = ['uint8', 'float16'];
  image_data_pixel_formats = ['uint8', 'uint16', 'float32'];
  var passed = true;
  for (var i = 0; i < 3; ++i) {
    for (var j = 0; j < 2; ++j) {
      for (var k = 0; k < 3; ++k) {
        for (var l = 0; l < 3; ++l) {
          if (!run_test(color_spaces[i],
                        canvas_pixel_formats[j],
                        color_spaces[k],
                        image_data_pixel_formats[l])) {
            passed = false;
          }
        }
      }
    }
  }
  assert_true(passed);
}
</script>
</body>
